/******************************************************************************* * Copyright (c) 2004, 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.internal.layout; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Layout; /** * This class wraps a control with a complex computeSize method. It uses caching * to reduce the number of times the control's computeSize method is called. This * allows controls (such as Coolbars and wrapping text) with slow computeSize * operations to be used inside layouts and composites that use inefficient caching. * <p> * For example, you want to use Coolbar A inside composite B. Rather than making A * a direct child of B, place it inside a CacheWrapper and insert the CacheWrapper * into B. Any layout data that would normally be attached to the control itself * should be attached to the wrapper instead: * </p> * <code> * * // Unoptimized code * Toolbar myToolbar = new Toolbar(someParent, SWT.WRAP); * myToolbar.setLayoutData(someLayoutData); * </code> * <code> * * // Optimized code * CacheWrapper myWrapper = new CacheWrapper(someParent); * Toolbar myToolbar = new Toolbar(myWrapper.getControl(), SWT.WRAP); * myWrapper.getControl().setLayoutData(someLayoutData); * </code> * <p> * CacheWrapper creates a Composite which should have exactly one child: the control * whose size should be cached. Note that CacheWrapper does NOT respect the flushCache * argument to layout() and computeSize(). This is intentional, since the whole point of * this class is to workaround layouts with poor caching, and such layouts will typically * be too eager about flushing the caches of their children. However, this means that you * MUST manually call flushCache() whenver the child's preferred size changes (and before * the parent is layed out). * </p> */ public class CacheWrapper { private Composite proxy; private SizeCache cache = new SizeCache(); private Rectangle lastBounds = new Rectangle(0, 0, 0, 0); private class WrapperLayout extends Layout implements ICachingLayout { protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { Control[] children = composite.getChildren(); if (children.length != 1) { return new Point(0, 0); } cache.setControl(children[0]); return cache.computeSize(wHint, hHint); } protected void layout(Composite composite, boolean flushCache) { Control[] children = composite.getChildren(); if (children.length != 1) { return; } Control child = children[0]; Rectangle newBounds = composite.getClientArea(); if (!newBounds.equals(lastBounds)) { child.setBounds(newBounds); lastBounds = newBounds; } } /* (non-Javadoc) * @see org.eclipse.ui.internal.layout.ICachingLayout#flush(org.eclipse.swt.widgets.Control) */ public void flush(Control dirtyControl) { CacheWrapper.this.flushCache(); } } /** * Creates a <code>CacheWrapper</code> with the given parent * * @param parent */ public CacheWrapper(Composite parent) { proxy = new Composite(parent, SWT.NONE); proxy.setLayout(new WrapperLayout()); } /** * Flush the cache. Call this when the child has changed in order to force * the size to be recomputed in the next resize event. */ public void flushCache() { cache.flush(); } /** * Use this as the parent of the real control. * * @return the proxy contol. It should be given exactly one child. */ public Composite getControl() { return proxy; } /** * Dispose of any widgets created by this wrapper. */ public void dispose() { if (proxy != null) { proxy.dispose(); proxy = null; } } }